#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <openssl/md5.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "edge-update-Use/cJSON.h"

#define BUFFER_SIZE 1024 // 定义缓冲区大小为 1024 字节
#define UPDATE_PORT 8000 // 定义更新端口号为 8000
#define UPDATE_FILE_PATH "/tmp/update_package.zip"
#define UNZIP_DIR "/tmp/update"

// 计算文件的MD5值
int calculateFileMD5(const char *file_path, unsigned char *md5_result)
{
    FILE *fp = fopen(file_path, "rb"); // 打开文件
    // 容错判断
    if (fp == NULL)
    {
        perror("打开MD5文件失败");
        return -1;
    }
    MD5_CTX md5_ctx;                                             // 创建MD5上下文
    MD5_Init(&md5_ctx);                                          // 初始化MD5上下文
    unsigned char buffer[BUFFER_SIZE];                           // 定义缓冲区
    size_t bytes_read;                                           // 定义读取的字节数
    while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, fp)) > 0) // 读取文件
    {
        MD5_Update(&md5_ctx, buffer, bytes_read); // 更新MD5上下文
    }
    MD5_Final(md5_result, &md5_ctx); // 计算MD5值
    fclose(fp);                      // 关闭文件
    return 0;
}

// 升级线程函数
void *updatePthread(void *arg)
{
    (void)arg; // 忽略参数
    pthread_detach(pthread_self()); // 分离线程
    printf("升级系统已连接\n");
    char buf[BUFFER_SIZE] = {0};                         // 定义缓冲区
    long md5_size = 0;                                   // 定义md5大小
    char md5_result[33] = {0};                           // 定义MD5值
    int len, total_len = 0;                              // 定义长度
    int update_sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
    // 容错判断
    if (update_sockfd < 0)
    {
        perror("TCP套接字创建失败");
        pthread_exit(NULL); // 退出线程
    }
    // 设置IP地址复用选项
    int ret;
    int optval = 1;
    setsockopt(update_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    // 绑定网络信息
    struct sockaddr_in update_addr, client_addr;
    memset(&update_addr, 0, sizeof(update_addr)); // 清空地址结构体
    update_addr.sin_family = AF_INET;             // 设置地址族为 IPv4
    update_addr.sin_addr.s_addr = INADDR_ANY;     // 监听所有网络接口
    update_addr.sin_port = htons(8000);           // 设置端口号，htons 用于将主机字节序转换为网络字节序
    socklen_t update_len = sizeof(struct sockaddr_in);
    // 绑定套接字
    if (bind(update_sockfd, (struct sockaddr *)&update_addr, update_len) < 0)
    {
        perror("TCP套接字绑定失败");
        close(update_sockfd); // 关闭套接字
        pthread_exit(NULL);   // 退出线程
    }
    // 监听套接字
    if (listen(update_sockfd, 5) < 0)
    {
        perror("TCP套接字监听失败");
        close(update_sockfd); // 关闭套接字
        pthread_exit(NULL);   // 退出线程
    }
    printf("等待客户端的连接\n");
    // 接受客户端连接
    int client_sockfd = accept(update_sockfd, (struct sockaddr *)&client_addr, &update_len);
    // 容错判断
    if (client_sockfd < 0)
    {
        perror("TCP套接字接受失败");
        close(update_sockfd); // 关闭套接字
        pthread_exit(NULL);   // 退出线程
    }
    printf("客户端连接成功\n");
    // 接收MD5值和文件长度
    if ((ret = recv(client_sockfd, buf, sizeof(buf), 0)) < 0)
    {
        perror("接收MD5值失败");
        close(update_sockfd); // 关闭套接字
        close(client_sockfd); // 关闭套接字
        pthread_exit(NULL);   // 退出线程
    }
    else if (ret == 0)
    {
        printf("客户端关闭连接\n");
        close(update_sockfd); // 关闭套接字
        close(client_sockfd); // 关闭套接字 
    }
    printf("%s\n", buf);
    for (int i = 9, j = 0; i < 41, j < 32; i++, j++)
    {
        md5_result[j] = buf[i];
    }
    md5_result[32] = '\0';
    printf("%s\n", md5_result);
    char recv_size[8] = {0};
    for (int i = 0, j = 0; i < 9, j < 8; i++, j++)
    {
        recv_size[j] = buf[i];
    }
    md5_size = atol(recv_size);
    printf("md5_size = %ld\n", md5_size);
    memset(buf, 0, sizeof(buf));
    // 打开目标文件
    int file_fd = open(UPDATE_FILE_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0644); // 打开文件
    // 容错判断
    if (file_fd < 0)
    {
        perror("打开文件失败");
        close(update_sockfd); // 关闭套接字
        close(client_sockfd); // 关闭套接字
        pthread_exit(NULL);   // 退出线程
    }
    printf("开始接收文件\n");
    // 接收文件
    while (1)
    {
        len = recv(client_sockfd, buf, BUFFER_SIZE, 0);
        if (len <= 0)
        { // 添加错误检查
            perror("接收错误或连接关闭");
            break;
        }
        ssize_t written = write(file_fd, buf, len);
        if (written != len)
        {
            perror("写入文件不完整");
            break;
        }
        total_len += len;
        if (total_len >= md5_size)
        {
            break;
        }
        memset(buf, 0, sizeof(buf));
    }
    printf("文件接收完成，文件大小为：%d\n", total_len);
    // 关闭文件
    close(file_fd);
    // 关闭套接字
    close(update_sockfd);
    close(client_sockfd);
    // 验证文件大小
    struct stat file_stat;
    if (stat(UPDATE_FILE_PATH, &file_stat) == -1)
    {
        perror("获取文件失败");
    }
    int size_valid = 0;
    size_valid = (file_stat.st_size == md5_size);
    printf("文件大小为 %s\n", size_valid ? "PASS" : "FAIL");
    // 验证MD5值
    unsigned char actual_md5[MD5_DIGEST_LENGTH];
    int md5_valid = 1;
    if (calculateFileMD5(UPDATE_FILE_PATH, actual_md5) == 0)
    {
        printf("文件的MD5值为: %s\n", actual_md5);
        for (int i = 16, j = 0; i < 48, j < 32; i++, j++)
        {
            printf("%c", actual_md5[i]);
            if (md5_result[j] != actual_md5[i])
            {
                md5_valid = 0;
            }
        }
        printf("文件的MD5值为: %s\n", md5_valid ? "PASS" : "FAIL");
    }
    else
    {
        printf("获取文件MD5值失败\n");
    }
    // 解压文件
    if (size_valid && md5_valid)
    {
        printf("文件验证通过，开始解压文件\n");
        char command[256];
        snprintf(command, sizeof(command), "unzip -o %s -d %s", UPDATE_FILE_PATH, UNZIP_DIR); // 构建解压命令
    }
    else
    {
        printf("文件验证失败，无法解压文件\n");
    }
    mkdir(UNZIP_DIR, 0777); // 创建解压目录
    char command[256];
    snprintf(command, sizeof(command), "unzip -o %s -d %s", UPDATE_FILE_PATH, UNZIP_DIR); // 构建解压命令
    int result = system(command);                                                         // 执行解压命令
    if (result == 0)
    {
        printf("固件升级成功\n");
    }
    else
    {
        printf("固件升级失败\n");
    }
    pthread_exit(NULL); // 退出线程
    return NULL;
}

// 重启进程
void restart_process()
{
    pid_t pid = fork();
    if (pid == 0)
    {
        // 子进程
        char *args[] = {"/mnt/hgfs/gateWay/search_response", NULL}; 
        execv(args[0], args);
        perror("execv failed"); // 打印错误信息
        _exit(EXIT_FAILURE);    // 如果execv失败
    }
    else if (pid > 0)
    {
        // 父进程
        int status;
        waitpid(pid, &status, 0); // 等待子进程结束
        exit(EXIT_SUCCESS);       // 结束当前进程
    }
    else
    {
        // fork失败
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
}

// 设备搜索进程
int main(int argc, char const *argv[])
{
    // 接收广播包
    char client_buf[2048] = {0}; // 用于存储从QT客户端接收到的 JSON 数据，初始化为 0
    char json_buf[4096] = {0};   // 用于存储从 JSON 数据中提取的 数据，初始化为 0
    ssize_t ret = 0;             // 用于存储系统调用的返回值，如 recvfrom、sendto 等
    // 先引用cjson库
    FILE *fp = fopen("edge-update-Use/pointTable.json", "r"); // 打开cjson文件夹
    // 容错判断
    if (fp == NULL)
    {
        perror("打开cjson文件失败");
        return -1;
    }
    // 获取文件大小
    ssize_t file_size = fread(json_buf, 1, sizeof(json_buf), fp);
    // 容错判断
    if (file_size < 0)
    {
        perror("读取cjson文件失败");
        return -1;
    }
    // 关闭文件
    fclose(fp);
    cJSON *root = cJSON_Parse(json_buf); // 解析json文件
    // 容错判断
    if (root == NULL)
    {
        perror("解析cjson文件失败");
        return -1;
    }
    cJSON *version = cJSON_GetObjectItem(root, "version"); // 获取version
    // 容错判断
    if (version == NULL)
    {
        perror("获取version失败");
        return -1;
    }
    char recv_buf[128] = {0};                                               // 用于存储从QT客户端接收到的数据，初始化为 0
    char send_buf[5] = {'h', 'e', 'l', 'l', 'o'};                           // 用于存储发送给QT客户端的数据，初始化为 "hello"并避免‘\0’的影响
    char update_buf[6] = {'u', 'p', 'd', 'a', 't', 'e'};                    // 用于存储发送给QT客户端的数据，初始化为 "update"并避免‘\0’的影响
    char un_update_buf[10] = {'u', 'n', 'u', 'p', 'd', 'a', 't', 'e', 'd'}; // 用于存储发送给QT客户端的数据，初始化为 "unupdated"并避免‘\0’的影响
    int optval = 1;                                                         // 设置IP地址复用选项的值为 1，用于允许在同一端口上重复使用地址
    // 创建 UDP 套接字
    int udp_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    // 容错判断
    if (udp_sockfd < 0)
    {
        perror("UDP套接字创建失败");
        return -1;
    }
    // 绑定网络信息
    struct sockaddr_in device_addr, client_addr;
    device_addr.sin_family = AF_INET;         // 设置地址族为 IPv4
    device_addr.sin_port = htons(8887);       // 设置端口号，htons 用于将主机字节序转换为网络字节序
    device_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有可用的本地地址
    socklen_t len = sizeof(struct sockaddr_in);
    // 设置IP地址复用选项
    if (setsockopt(udp_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
    {
        perror("设置IP地址复用选项失败");
        return -1;
    }
    if (bind(udp_sockfd, (struct sockaddr *)&device_addr, sizeof(device_addr)) < 0)
    {
        perror("UDP绑定失败");
        return -1;
    }

    // 进入循环，持续接收来自QT客户端的信息
    while (1)
    {
        // 接收来自QT客户端端的信息
        ret = recvfrom(udp_sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&client_addr, &len);
        // 容错判断
        if (ret < 0)
        {
            perror("设备接收失败");
            return -1;
        }

        // 检查接收到的信息是否为目标信息
        if (strcmp(recv_buf, "hello gateWay") == 0)
        {
            printf("这是我们的客户\n"); // 打印连接成功信息
            // 回复信息给 QT客户端
            sendto(udp_sockfd, send_buf, sizeof(send_buf), 0, (struct sockaddr *)&client_addr, sizeof(client_addr));
            break; // 跳出循环
        }
        else
        {
            printf("这不是我们的客户,已忽略\n"); // 打印非目标客户端信息
            continue;                            // 继续下一次循环
        }

        // 清空接收缓冲区
        memset(recv_buf, 0, sizeof(recv_buf));
    }

    // 此时已与QT客户端建立 UDP 连接
    // 创建 TCP 套接字
    int tcp_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    // 设置IP地址复用选项
    if (setsockopt(tcp_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
    {
        perror("设置IP地址复用选项失败");
        return -1;
    }
    // 容错判断
    if (tcp_sockfd < 0)
    {
        perror("TCP套接字创建失败");
        return -1;
    }

    // 绑定网络信息
    if (bind(tcp_sockfd, (struct sockaddr *)&device_addr, sizeof(device_addr)) < 0)
    {
        perror("TCP绑定失败");
        return -1;
    }

    // 监听端口
    if (listen(tcp_sockfd, 10) < 0)
    {
        perror("TCP监听失败");
        return -1;
    }

    printf("等待QT客户端连接\n");
    int acceptfd = accept(tcp_sockfd, (struct sockaddr *)&client_addr, &len);
    // 容错判断
    if (acceptfd < 0)
    {
        perror("创建accept套接字失败");
        return -1;
    }
    printf("客户端连接成功\n"); // 打印客户端连接成功信息
    close(udp_sockfd);
    // 进行OTA升级
    while (1)
    {
        memset(recv_buf, 0, sizeof(recv_buf));
        // 接收来自QT客户端端的信息
        ret = recvfrom(acceptfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&client_addr, &len);
        // 容错判断
        if (ret < 0)
        {
            perror("设备接收失败");
            return -1;
        }
        else if (ret == 0)
        {
            printf("客户端断开连接\n"); // 打印客户端断开连接信息
            continue;
        }
        if (strcmp(recv_buf, version->valuestring) == 0)
        {
            printf("版本一致无需升级\n"); // 打印版本一致信息
            send(acceptfd, un_update_buf, sizeof(un_update_buf), 0);
            continue;
        }
        else
        {
            printf("版本不一致，需要升级\n"); // 打印版本不一致信息
            send(acceptfd, update_buf, sizeof(update_buf), 0);
            pthread_t tid;                                            // 用于存储线程 ID 的变量
            if (pthread_create(&tid, NULL, updatePthread, NULL) != 0) // 创建线程
            {
                perror("线程创建失败"); // 打印线程创建失败信息
                return -1;              // 返回错误码
            }
            pthread_join(tid, NULL); // 等待线程结束
            break;
        }
    }
    close(acceptfd);
    close(tcp_sockfd);
    restart_process();       // 重启进程
    return 0;
}